package com.caimiao.miao.library.usb.accessory;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;

import com.caimiao.miao.library.utils.tip.ToastUtil;

import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

import io.reactivex.Observable;
import io.reactivex.disposables.Disposable;
import io.reactivex.functions.Consumer;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;

public class UsbAccessoryInterface {
    private static final String TAG = UsbAccessoryInterface.class.getSimpleName();
    public static final int REQUEST_USB_PERMISSION = 1002;
    private static final String ACTION_USB_PERMISSION = "com.caimiao.miao.USB_PERMISSION";
    private UsbManager mUsbManager;
    private UsbAccessoryReceiver mUsbReceiver;
    private PendingIntent mPermissionIntent;
    private boolean mPermissionRequestPending = false;//是否申请过USB权限
    private ParcelFileDescriptor mFileDescriptor;
    private FileOutputStream mOutputStream;
    private FileInputStream mInputStream;
    private Disposable mDisposable;
    private volatile byte[] mReadBuffer;
    private int mReadInterval;

    private class UsbAccessoryReceiver extends BroadcastReceiver {

        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            if (ACTION_USB_PERMISSION.equals(action)) {
                synchronized (this) {
                    UsbAccessory accessory = intent.getParcelableExtra(UsbManager.EXTRA_ACCESSORY);
                    if (accessory != null) {
                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                            ToastUtil.show("获取USB权限成功");
                            openAccessory(accessory);
                        } else {
                            ToastUtil.show("获取USB权限失败");
                        }
                    }
                    mPermissionRequestPending = false;
                }
            } else if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
                ToastUtil.show("插入USB设备");
//                resumeAccessory();
            } else if (UsbManager.ACTION_USB_ACCESSORY_DETACHED.equals(action)) {
                ToastUtil.show("拔出USB设备");
                destroyAccessory();
            }
        }
    }

    public UsbAccessoryInterface(Context context) {
        this(context, 100);
    }

    public UsbAccessoryInterface(Context context, int readInterval) {
        mReadInterval = readInterval;
        mUsbManager = (UsbManager) context.getSystemService(Context.USB_SERVICE);
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
        filter.addAction(UsbManager.ACTION_USB_ACCESSORY_DETACHED);
        mUsbReceiver = new UsbAccessoryReceiver();
        context.registerReceiver(mUsbReceiver, filter);
        mPermissionIntent = PendingIntent.getBroadcast(context, REQUEST_USB_PERMISSION, new Intent(ACTION_USB_PERMISSION), 0);
    }

    public List<UsbAccessory> getAccessories() {
        List<UsbAccessory> accessoryList = new ArrayList<>();
        UsbAccessory[] accessories = mUsbManager.getAccessoryList();
        int length = accessories == null ? 0 : accessories.length;
        ToastUtil.show("USB从机个数：" + length);
        for (int i = 0; i < length; i++) {
            accessoryList.add(accessories[i]);
        }
        return accessoryList;
    }

    public UsbAccessory getAccessory(String manufacturer, String model, String version) {
        UsbAccessory[] accessories = mUsbManager.getAccessoryList();
        int length = accessories == null ? 0 : accessories.length;
        for (int i = 0; i < length; i++) {
            UsbAccessory accessory = accessories[i];
            if (TextUtils.equals(accessory.getManufacturer(), manufacturer)
                    && TextUtils.equals(accessory.getModel(), model)
                    && TextUtils.equals(accessory.getVersion(), version)) {
                return accessory;
            }
        }
        return null;
    }


    public void onDestroy(Context context) {
        context.unregisterReceiver(mUsbReceiver);
        destroyAccessory();
    }

    public void send(char[] data) {
        int length = data == null ? 0 : data.length;
        if (length == 0) return;
        byte[] buffer = new byte[length];
        for (int i = 0; i < length; i++) {
            buffer[i] = (byte) data[i];
        }
        sendData(buffer);
    }

    public byte[] read() {
        return mReadBuffer;
    }

    public void removeReadBuffer(int count) {
        removeReadBuffer(0, count);
    }

    public void removeReadBuffer(int start, int count) {
        if (start < 0) return;
        synchronized (mReadBuffer) {
            int length = mReadBuffer == null ? 0 : mReadBuffer.length;
            if (length == 0) return;
            byte[] temp;
            if (start == 0 && start + count >= length) {
                mReadBuffer = null;//全清
            } else if (start == 0 && start + count < length) {
                temp = new byte[length - count];
                System.arraycopy(mReadBuffer, count, temp, 0, length - count);
                mReadBuffer = temp;//清前部分
            } else if (start > 0 && start + count >= length) {
                temp = new byte[start];
                System.arraycopy(mReadBuffer, 0, temp, 0, start);
                mReadBuffer = temp;//清后部分
            } else {
                temp = new byte[length - count];
                //原数组， 原数组的开始位置， 目标数组， 目标数组的开始位置， 拷贝个数
                System.arraycopy(mReadBuffer, 0, temp, 0, start);
                System.arraycopy(mReadBuffer, start + count, temp, start, length - start - count);
                mReadBuffer = temp;//清中间部分
            }
        }
    }

    public void resumeAccessory() {
        if (mInputStream != null && mOutputStream != null)
            return;
        UsbAccessory accessory = getAccessory("", "", "");
        if (accessory == null) {
            ToastUtil.show("USB未插入");
            return;
        }
        if (mUsbManager.hasPermission(accessory)) {
            openAccessory(accessory);
        } else {
            synchronized (mUsbReceiver) {
                if (!mPermissionRequestPending) {
                    mUsbManager.requestPermission(accessory, mPermissionIntent);
                    mPermissionRequestPending = true;
                }
            }
        }
    }

    private void openAccessory(UsbAccessory accessory) {
        mFileDescriptor = mUsbManager.openAccessory(accessory);
        if (mFileDescriptor != null) {
            FileDescriptor fd = mFileDescriptor.getFileDescriptor();
            mInputStream = new FileInputStream(fd);
            mOutputStream = new FileOutputStream(fd);
            if ((mInputStream == null || mOutputStream == null)) {
                return;
            }
            startReadLooper();
        }
    }

    private void destroyAccessory() {
        try {
            if (mFileDescriptor != null)
                mFileDescriptor.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            if (mInputStream != null)
                mInputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }

        try {
            if (mOutputStream != null)
                mOutputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        mFileDescriptor = null;
        mInputStream = null;
        mOutputStream = null;

        if (mDisposable != null)
            mDisposable.dispose();
        mDisposable = null;
    }

    private void sendData(byte[] bytes) {
        try {
            if (mOutputStream != null)
                mOutputStream.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void startReadLooper() {
        mDisposable = Observable.interval(0, mReadInterval, TimeUnit.MILLISECONDS)
                .map(new Function<Long, ReadBuffer>() {
                    @Override
                    public ReadBuffer apply(Long aLong) throws Exception {
                        ReadBuffer buffer = new ReadBuffer();
                        buffer.count = mInputStream.read(buffer.buffer, 0, ReadBuffer.READ_LENGTH);
                        return buffer;
                    }
                })
                .subscribeOn(Schedulers.io())
                .subscribe(new Consumer<ReadBuffer>() {
                    @Override
                    public void accept(ReadBuffer readBuffer) throws Exception {
                        if (readBuffer.count == 0) return;
                        synchronized (mReadBuffer) {
                            int length = mReadBuffer == null ? 0 : mReadBuffer.length;
                            byte[] temp = new byte[readBuffer.count + length];
                            if (length > 0)
                                System.arraycopy(mReadBuffer, 0, temp, 0, length);
                            //原数组， 原数组的开始位置， 目标数组， 目标数组的开始位置， 拷贝个数
                            System.arraycopy(readBuffer.buffer, 0, temp, length, readBuffer.count);
                            mReadBuffer = temp;
                        }
                    }
                }, new Consumer<Throwable>() {
                    @Override
                    public void accept(Throwable throwable) throws Exception {
                        Log.e(TAG, throwable.getMessage());
                    }
                });
    }

    private class ReadBuffer {
        private static final int READ_LENGTH = 1024;
        private int count;
        private byte[] buffer = new byte[READ_LENGTH];
    }
}
